/*
 * Water.cpp
 *
 *  Created on: 23.10.2012
 *      Author: Martin
 */

#ifdef _WIN32 
#include <windows.h> 
#endif

#include "Water.h"

Water::Water(float _high, float _size, int _div)
{
#ifdef _DEBUG
	std::cout << "WATER: Init" << std::endl;
#endif
	high = _high;
	size = _size;
	div = _div;
	texPath = Engine::fileStructure.texturePath + Engine::mapFileStruct.waterPath;

	// loading textures and shaders for water mesh
	textures = new TexPool(texPath.c_str());

	textures->loadDir();

	shaderID = Shader::sPool->getProgramID("water");

	normalTextureID = textures->getTexIdByName("watern.jpg");

	/*
	 setVectors();
	 indexing();

	 createBufferObject(&VertexArrayID, &normalBufferID, &uvbuffer, &elementBufferID);
	 iSizeOld = fillBufferObject(VertexArrayID, normalBufferID, uvbuffer, elementBufferID);*/
}

Water::~Water()
{
	// delete buffers
	glDeleteBuffers(1, &VertexArrayID);
	glDeleteBuffers(1, &uvbuffer);
	glDeleteBuffers(1, &normalBufferID);
	glDeleteBuffers(1, &elementBufferID);
}

void Water::setVertex(float x, float z)
{
	buf_normals.push_back(glm::vec3(0.0, 1.0, 0.0));
	buf_uvs.push_back(glm::vec2(x, z));
	buf_vertices.push_back(glm::vec3(x, high, z));
}

int Water::setVectors(float x, float z, float s)
{
	// t1
	setVertex(x, z);
	setVertex(x, z + s);
	setVertex(x + s, z);

	// t2
	setVertex(x + s, z);
	setVertex(x + s, z + s);
	setVertex(x, z + s);

	return 0;
}

int Water::setVectors()
{
	// calculate step
	float step = size / div;

	float _x = 0.0;
	float _z = 0.0;

	for (int x = 0; x < div; x++)
	{

		for (int y = 0; y < div; y++)
		{

			// v1
			setVertex(_x, _z);

			_x = _x + step;

			// v2
			setVertex(_x, _z);

			_x = _x - step;
			_z = _z + step;

			// v3
			setVertex(_x, _z);

			// v3 for next triangle
			setVertex(_x, _z);

			_x = _x + step;
			// v4 for next triangle
			setVertex(_x, _z);

			_z = _z - step;

			// v2 for next triangle
			setVertex(_x, _z);

			_z = _z + step;
			_x = _x - step;
		}
		_x = _x + step;
		_z = 0.0;

	}

	return 0;
}

void Water::indexing()
{
	// indexing
	// external function from special/VBOIndexer.h file
	// so this is't member of class terrain
	indexVBO(buf_vertices, buf_uvs, buf_normals, indices, indexed_vertices,
			indexed_uvs, indexed_normals);

	// clear this values are useless
	buf_vertices.clear();
	buf_uvs.clear();
	buf_normals.clear();
}

void Water::createBufferObject(GLuint *v, GLuint *n, GLuint *uv, GLuint *e)
{
	// vertex buffer
	glGenBuffers(1, v);

	// normal buffer
	glGenBuffers(1, n);

	// uv coords buffer
	glGenBuffers(1, uv);

	// indexing, element buffer
	glGenBuffers(1, e);
}

int Water::fillBufferObject(GLuint v, GLuint n, GLuint uv, GLuint e)
{
	glBindBuffer(GL_ARRAY_BUFFER, v);
	glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3),
			&indexed_vertices[0], GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, n);
	glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3),
			&indexed_normals[0], GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, uv);
	glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2),
			&indexed_uvs[0], GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, e);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short),
			&indices[0], GL_STATIC_DRAW);

	int out = indices.size();

	// not used anymore
	indexed_vertices.clear();
	indexed_uvs.clear();
	indexed_normals.clear();
	indices.clear();

	return out;
}

void Water::draw(GLuint refTexture, GLuint refractionTexture)
{
	draw(refTexture, refractionTexture, VertexArrayID, normalBufferID, uvbuffer,
			elementBufferID, iSizeOld);
}

void Water::draw(GLuint refTexture, GLuint refractionTexture, GLuint v,
		GLuint n, GLuint uv, GLuint e, int isize)
{
	float time = (float) SDL_GetTicks() / 2000.0;

	// get matrix
	glm::mat4 ModelMatrix = glm::mat4(1.0);
	glm::mat4 ViewMatrix = World::world->getView();
	//ViewMatrix = glm::rotate(ViewMatrix,45.0f, glm::vec3(0.0f, 1.0f ,0.0f));
	glm::mat4 MVP = World::world->getProjection() * ViewMatrix * ModelMatrix;

	// get light information
	glm::vec3 LightPos = World::world->getLightPos(); // position of light
	glm::vec3 LightDif = World::world->getLightDif(); // diffuse color of light
	glm::vec3 LightAmb = World::world->getLightAmb(); // ambient color of light
	glm::vec3 LightSpec = World::world->getLightSpe(); // specular color of light

	glUseProgram(shaderID);

	// send matrix to shader

	glUniform1i(glGetUniformLocation(shaderID, "normalTexture"), 1);
	glUniform1i(glGetUniformLocation(shaderID, "refTexture"), 2);
	glUniform1i(glGetUniformLocation(shaderID, "refractionTexture"), 3);

	glUniform1f(glGetUniformLocation(shaderID, "time"), time);

	glUniformMatrix4fv(glGetUniformLocation(shaderID, "MVP"), 1, GL_FALSE,
			&MVP[0][0]);
	glUniformMatrix4fv(glGetUniformLocation(shaderID, "M"), 1, GL_FALSE,
			&ModelMatrix[0][0]);
	glUniformMatrix4fv(glGetUniformLocation(shaderID, "V"), 1, GL_FALSE,
			&ViewMatrix[0][0]);
	glUniform3f(glGetUniformLocation(shaderID, "LightPosition_worldspace"),
			LightPos.x, LightPos.y, LightPos.z);
	glUniform3f(glGetUniformLocation(shaderID, "lightAmb"), LightAmb.x,
			LightAmb.y, LightAmb.z);
	glUniform3f(glGetUniformLocation(shaderID, "lightDif"), LightDif.x,
			LightDif.y, LightDif.z);
	glUniform3f(glGetUniformLocation(shaderID, "lightSpec"), LightSpec.x,
			LightSpec.y, LightSpec.z);
	glUniform2f(glGetUniformLocation(shaderID, "ViewPortSize"),
			Engine::screenSettings.screenWidth, Engine::screenSettings.screenHeight);

	glEnable(GL_BLEND);

	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, v);
	glVertexAttribPointer(0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
			3, // size
			GL_FLOAT, // type
			GL_FALSE, // normalized?
			0, // stride
			(void*) 0 // array buffer offset
	);

	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, uv);
	glVertexAttribPointer(1, // attribute 0. No particular reason for 0, but must match the layout in the shader.
			2, // size
			GL_FLOAT, // type
			GL_FALSE, // normalized?
			0, // stride
			(void*) 0 // array buffer offset
	);

	glEnableVertexAttribArray(2);
	glBindBuffer(GL_ARRAY_BUFFER, n);
	glVertexAttribPointer(2, // attribute 0. No particular reason for 0, but must match the layout in the shader.
			3, // size
			GL_FLOAT, // type
			GL_FALSE, // normalized?
			0, // stride
			(void*) 0 // array buffer offset
	);

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, normalTextureID);

	glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, refTexture);

	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_2D, refractionTexture);

	// Index buffer
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, e);

	// Draw the array !
	glDrawElements(GL_TRIANGLES, // mode
			isize, // count
			GL_UNSIGNED_SHORT, // type
			(void*) 0 // element array buffer offset
	);

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	glDisableVertexAttribArray(2);

	glDisable(GL_BLEND);

	glUseProgram(0);
}

float Water::getHigh()
{
	return high;
}
